import type { RouteComponent, RouteRecordRaw } from 'vue-router'
import JsonUtils from '@/utils/JsonUtils'
import HexUtils from '@/utils/HexUtils'

const pathComponentMap = new Map(Object.entries(import.meta.glob('@/views/**/*.{vue,tsx}')))
const getComponent = (path: string | RouteComponent): RouteComponent => {
  if (typeof path !== 'string') {
    // 如果传入的不是字符串，直接返回
    return path
  }
  // 确保路径以 .vue 结尾，如果不是，则添加
  const vuePath = path.endsWith('.vue') ? path : `${path}.vue`
  // 构建完整的视图路径
  const fullPath = vuePath.startsWith('/') ? `/src/views${vuePath}` : `/src/views/${vuePath}`
  // 尝试从 map 中获取组件
  const component = pathComponentMap.get(fullPath)
  // 如果找到了组件，返回它；否则返回 404 页面组件
  return component ?? (pathComponentMap.get('/src/views/error/404.vue') as RouteComponent)
}

export class MenuUtils {
  private static idCounter = 1

  /**
   * 取消树形节点之间的关联关系，用于序列化
   * @param menus
   */
  public static disconnectTreeNodeRelations<T extends MenuRecordRaw>(menus: Array<T>): Array<T> {
    //深克隆，需要先讲代理对象转换为普通对象
    menus = structuredClone(toRaw(menus))
    for (const menu of menus) {
      menu.parent = undefined
      menu.children = MenuUtils.disconnectTreeNodeRelations(menu.children || [])
    }
    return menus
  }

  /**
   * 序列化为字符串，解决了循环引用的问题
   * @param menus 菜单列表
   */
  public static stringify<T extends MenuRecordRaw>(menus: Array<T>): string {
    return JsonUtils.stringify(MenuUtils.disconnectTreeNodeRelations(menus))
  }

  /**
   * 被处理后的菜单列表，已经关联父级，返回值为原菜单列表，可能没有去重，如果要去重可以使用[...menuMap.values()]
   * @param menus 需要处理的菜单列表
   * @param menuMap 菜单ID和菜单的映射
   * @param routerSet 路由列表
   * @param keepAliveNameSet 需要缓存的路由名字
   */
  public static processList<T extends MenuRecordRaw>(
    menus: Array<T>,
    menuMap?: Map<string, T>,
    routerSet?: Set<RouteRecordRaw>,
    keepAliveNameSet?: Set<string>
  ): Array<T> {
    if (!Array.isArray(menus)) {
      return []
    }
    return MenuUtils.sort(
      menus.map((menu) => this.process(menu, undefined, menuMap, routerSet, keepAliveNameSet))
    )
  }

  /**
   * 菜单排序
   * @param menus
   */
  public static sort<T extends MenuRecordRaw>(menus: Array<T>) {
    return menus.sort((child1, child2) => (child1.sort as number) - (child2.sort as number))
  }

  /**
   * 返回菜单，已经关联父级的菜单，返回值为原菜单
   * @param menuItem 被处理的节点
   * @param parent 父节点
   * @param menuMap 菜单ID和菜单的映射
   * @param routerSet 路由列表
   * @param keepAliveNameSet 需要缓存的路由名字
   */
  public static process<T extends MenuRecordRaw>(
    menuItem: T,
    parent?: MenuRecordRaw,
    menuMap?: Map<string, T>,
    routerSet?: Set<RouteRecordRaw>,
    keepAliveNameSet?: Set<string>
  ): T {
    const id = menuItem.id || `${this.idCounter++}`
    menuItem.id = id
    menuItem.hidden = menuItem.hidden === true
    menuItem.disable = menuItem.disable === true
    menuItem.sort = menuItem.sort === undefined ? Number(id) : menuItem.sort
    const menuType = menuItem.menuType
    if (menuType === 'iframe') {
      menuItem.path = `/iframe/${HexUtils.strToHex(menuItem.oriURL)}`
    } else if (menuType === 'link') {
      menuItem.path = `/link/${HexUtils.strToHex(menuItem.oriURL)}`
    } else {
      menuItem.path = menuItem.path?.trim()?.length ? menuItem.path : `/${menuItem.id}`
    }
    menuItem.children = MenuUtils.sort(
      menuItem.children?.map((child) =>
        this.process(child, menuItem, menuMap, routerSet, keepAliveNameSet)
      ) || []
    )
    menuItem.parent = parent
    if (menuType !== 'dir') {
      if (menuType === 'router') {
        const routeItem: RouteItem = menuItem as RouteItem
        routeItem.isTopPage = routeItem.isTopPage === true
        routeItem.keepAlive = routeItem.keepAlive === true
        routeItem.isAffix = routeItem.isAffix === true
        const route: RouteRecordRaw = {
          path: menuItem.path,
          name: routeItem.name || `${id}`,
          component: getComponent(routeItem.component),
          meta: {
            id: id,
            isTopPage: routeItem.isTopPage,
            isAffix: routeItem.isAffix
          }
        }
        if (routeItem.keepAlive && routeItem.componentName) {
          keepAliveNameSet?.add(routeItem.componentName)
        }
        routerSet?.add(route)
      } else if (menuType === 'iframe') {
        const iframeItem: IframeItem = menuItem as IframeItem
        if (!iframeItem.oriURL?.length) {
          throw new Error('oriURL不能为空')
        }
        iframeItem.isAffix = iframeItem.isAffix === true
        const route: RouteRecordRaw = {
          path: menuItem.path,
          name: `${id}`,
          component: () => import('@/views/system/Iframe.vue'),
          meta: {
            id: id,
            isTopPage: false,
            isAffix: iframeItem.isAffix
          }
        }
        routerSet?.add(route)
      } else if (menuType === 'link') {
        const linkItem: LinkItem = menuItem as LinkItem
        if (!linkItem.oriURL?.length) {
          throw new Error('oriURL不能为空')
        }
        linkItem.isAffix = linkItem.isAffix === true
        const route: RouteRecordRaw = {
          path: menuItem.path,
          name: `${id}`,
          component: () => import('@/views/system/Link.vue'),
          meta: {
            id: id,
            isTopPage: false,
            isAffix: linkItem.isAffix
          }
        }
        routerSet?.add(route)
      }
    }
    menuMap?.set(id, menuItem)
    return menuItem
  }
}
